package resolver

// resolver的用途是将不同的注册中心获取到的数据，放到一个map中，并转换成负载均衡所需要的node

import (
	"context"
	"errors"
	log "gitee.com/vrv_media/go-micro-framework/pkg/logger"
	"gitee.com/vrv_media/go-micro-framework/registry"
)

// Resolve 服务发现解析器，一个服务名称有一个Resolver
type Resolve struct {
	serviceName string
	nodes       map[string]registry.Node
	discovery   registry.Discovery
	balancer    registry.Balancer
}

func NewResolve(serviceName string, discovery registry.Discovery, balancer registry.Balancer) *Resolve {
	resolve := &Resolve{
		serviceName: serviceName,
		nodes:       make(map[string]registry.Node),
		discovery:   discovery,
		balancer:    balancer,
	}
	err := resolve.Resolve(context.Background())
	if err != nil {
		panic(err)
	}
	return resolve
}

func (r *Resolve) Resolve(ctx context.Context) error {
	if r.serviceName == "" {
		return errors.New("service name cannot be empty")
	}
	if r.discovery == nil {
		return errors.New("discovery cannot be empty")
	}
	// 第一步上来以后，先调用GetService方法获取一次对象实例
	services, err := r.discovery.GetService(ctx, r.serviceName)
	if err != nil {
		log.ErrorF("get service error: %v", err)
		return err
	}
	if len(services) > 0 {
		r.generateServiceInstances(services...)
	}
	// 启用watch监听
	watch, err := r.discovery.Watch(ctx, r.serviceName)
	if err != nil {
		log.ErrorF("watch service error: %v", err)
		return err
	}
	if watch == nil {
		log.Info("get watch is nil")
		return nil
	}
	go func() {
		for {
			select {
			case <-ctx.Done():
				err := watch.Stop()
				if err != nil {
					return
				}
				return
			default:
				services, err := watch.Next()
				if err != nil {
					log.ErrorF("watch service error: %v", err)
					continue
				}
				if len(services) > 0 {
					r.generateServiceInstances(services...)
				}
			}
		}
	}()
	return nil
}

func (r *Resolve) generateServiceInstances(services ...*registry.ServiceInstance) {
	if len(services) == 0 {
		return
	}
	for _, service := range services {
		node := NewNodeWithServiceInstance(service)
		key := node.Key()
		// 直接放入到map中，如果key相同，则用最新的
		r.nodes[key] = node
	}
}

func (r *Resolve) Next(ctx context.Context) (selected registry.Node, err error) {
	if r.balancer == nil {
		return nil, errors.New("balancer cannot be empty")
	}
	nodes := make([]registry.Node, 0, len(r.nodes))
	for _, node := range r.nodes {
		nodes = append(nodes, node)
	}
	return r.balancer.Pick(ctx, nodes)
}
